﻿using Abp.Application.Services;
using System;
using System.Linq.Dynamic;
using FutureApp.Authorization.Dto;
using FutureApp.Authorization.Permissions;
using Abp.Domain.Repositories;
using System.Threading.Tasks;
using Abp.UI;
using System.Linq;
using Abp.Auditing;
using Abp.Application.Services.Dto;
using Abp.Authorization;

namespace FutureApp.Authorization
{
    /// <AppService/>
    [DisableAuditing]
    public class PermissionDefinitionAppService
        : AsyncCrudAppService<PermissionDefinition,
            PermissionDefinitionEntityDto,
            Guid,
            GetAllPermissionDefinitionInput,
            CreatePermissionDefinitionInput,
            UpdatePermissionDefinitionInput,
            GetPermissionDefinitionInput,
            DeletePermissionDefinitionInput>
        , IPermissionDefinitionAppService
    {
        /// <constructor/>
        public PermissionDefinitionAppService(IRepository<PermissionDefinition, Guid> repository)
            : base(repository)
        {
            this.LocalizationSourceName = FutureAppConsts.LocalizationSourceName;
        }

        /// <inheritDoc/>
        public override async Task<PermissionDefinitionEntityDto> Create(CreatePermissionDefinitionInput input)
        {
            var entity = this.MapToEntity(input);
            if (entity.ParentId.HasValue)
            {
                var parent = this.Repository.Get(entity.ParentId.Value);
                entity.Code = $"{parent?.Code}.{entity.Code}".TrimStart('.');
            }

            if (await this.Exist(entity.Code))
                throw new UserFriendlyException(L("ExistedPermission"));

            entity = await this.Repository.InsertAsync(entity);
            await this.CurrentUnitOfWork.SaveChangesAsync();

            return this.MapToEntityDto(entity);
        }

        private async Task<bool> Exist(Guid id)
        {
            return 0 != await this.Repository.CountAsync(e => e.Id == id);
        }

        private async Task<bool> Exist(string code)
        {
            return 0 != await this.Repository.CountAsync(e => e.Code == code);
        }

        /// <inheritDoc/>
        public override async Task Delete(DeletePermissionDefinitionInput input)
        {
            if (!await this.Exist(input.Id))
                throw new UserFriendlyException(L("NotExistedPermission"));
            if (await this.Repository.CountAsync(e => e.ParentId == input.Id) != 0)
                throw new UserFriendlyException(L("CannotDeleteUsedPermission"));

            await base.Delete(input);
        }

        /// <inheritDoc/>
        public override Task<PagedResultDto<PermissionDefinitionEntityDto>> GetAll(GetAllPermissionDefinitionInput input)
        {
            return base.GetAll(input);
        }

        /// <inheritDoc/>
        protected override PermissionDefinitionEntityDto MapToEntityDto(PermissionDefinition entity)
        {
            return this.MapToEntityDto(entity, NodeType.Current);
        }

        private PermissionDefinitionEntityDto MapToEntityDto(PermissionDefinition entity, NodeType type)
        {
            var dto = new PermissionDefinitionEntityDto
            {
                Id = entity.Id,
                Code = entity.Code,
                Name = entity.Name,
                Summary = entity.Summary,
                MultiTenancySides = entity.MultiTenancySides
            };
            if (type != NodeType.Parent)
            {
                dto.Children = entity.Children?.Select(child => this.MapToEntityDto(child, NodeType.Child)).ToArray();
            }
            if (type != NodeType.Child)
            {
                if (entity.Parent != null) dto.Parent = this.MapToEntityDto(entity.Parent, NodeType.Parent);
            }

            return dto;
        }

        private enum NodeType
        {
            Current, Parent, Child
        }
    }
}
